package top.mable.interceptors;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.reflect.FieldUtils;
import org.apache.ibatis.executor.resultset.ResultSetHandler;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Signature;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;
import top.mable.annotation.EncryptDecryptClass;
import top.mable.annotation.EncryptDecryptField;
import top.mable.utils.AESUtil;
import top.mable.utils.ReflectUtil;

import java.lang.reflect.Field;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
 * 使用 ResultSetHandler 对mybatis查询的结果进行解密处理  另外加密方式必须注意，不然由字节转为字符的时候出现问题
 * 新建注解的原因是：针对指定的类的指定字段才做操作
 *
 * @author taffywen
 * @date 2022/3/11 14:25
 */
@Slf4j
@Component
@Intercepts(@Signature(type = ResultSetHandler.class,method = "handleResultSets",args = {Statement.class} ))
public class MyDecryptInterceptor implements Interceptor {


    @Override
    public Object intercept(Invocation invocation) throws Throwable {

        log.info("进入DecryptInterceptor拦截器");
        Object result = invocation.proceed();
        if (ObjectUtil.isEmpty(result)){
            return result;
        }

        if (result instanceof List){
            ArrayList resultList = (ArrayList) result;
            if (CollectionUtil.isNotEmpty(resultList) && needToDecrypt(resultList.get(0))){
                for (int i = 0; i < resultList.size(); i++) {
                    decryptParam(resultList.get(i));
                }
            }
        }else {
            if (needToDecrypt(result)){
                decryptParam(result);
            }
        }

        return result;
    }

    public boolean needToDecrypt(Object object) {
        Class<?> objectClass = object.getClass();
        EncryptDecryptClass encryptDecryptClass = AnnotationUtils.findAnnotation(objectClass, EncryptDecryptClass.class);
        if (Objects.nonNull(encryptDecryptClass)){
            return true;
        }
        return false;
    }

    public void decryptParam(Object object) throws Exception {
        Class<?> objectClass = object.getClass();
        List<Field> declaredFields = ReflectUtil.getAllFieldsList(objectClass);
        for (Field field : declaredFields) {
            if (field.isAnnotationPresent(EncryptDecryptField.class) && field.getType().toString().endsWith("String")){
                field.setAccessible(true);
                String fieldValue = (String) field.get(object);
                if (StrUtil.isNotEmpty(fieldValue)){
                    field.set(object, AESUtil.decryptAES(fieldValue));
                }
                field.setAccessible(false);
            }
        }
    }
}
